package lv.imho.server.services.population;

import it.sauronsoftware.feed4j.FeedParser;
import it.sauronsoftware.feed4j.bean.Feed;
import it.sauronsoftware.feed4j.bean.FeedItem;
import it.sauronsoftware.feed4j.bean.RawElement;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.thoughtworks.xstream.XStream;

import lv.imho.client.model.DataSourceTransferObject;
import lv.imho.client.model.DataTransferObject;
import lv.imho.client.services.DataService;
import lv.imho.server.services.DataServiceImpl;
import lv.imho.server.services.population.entities.SOProcessingOrder;

/**
 * Service that is responsible for feed collection and processing orders.
 * 
 * @author Igors Gulbinskis
 */
public class DataPopulationOrderService extends HttpServlet {
	
	private static final Logger logger = Logger.getLogger(DataPopulationOrderService.class.getName());	
	private static final long   serialVersionUID = 1L;
	
	private final String SEMANTIC_ORIENTATION_WS_HOST = "ponijs.lv";
	private final int    SEMANTIC_ORIENTATION_WS_PORT = 8887;
	private final int    MAX_ORDERS = 10;
			
	private DataService dataService;
	
	public DataPopulationOrderService() {
		super();
		dataService = new DataServiceImpl();
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
		Calendar startDate = Calendar.getInstance();
		startDate.set(Calendar.YEAR, 2010);
		startDate.set(Calendar.MONTH, Calendar.MAY);
		startDate.set(Calendar.DATE, 1);
		startDate.set(Calendar.HOUR, 0);
		startDate.set(Calendar.MINUTE, 0);
		startDate.set(Calendar.SECOND, 0);
		
		List<SOProcessingOrder> orders = new ArrayList<SOProcessingOrder>();		
		int counter = MAX_ORDERS;		
		logger.info("request for data collection");
		
		URL url;
		int total = 0;
		String errors = "";
					
		List<DataSourceTransferObject> dataSources = dataService.getDataSources();
		if (dataSources != null
			&& !dataSources.isEmpty()) {
			/* iterate through data sources and check feeds */
			for (DataSourceTransferObject dataSource : dataSources) {
				if (counter == 0) {
					break;
				}
				
				/* feed4j have problems with date parsing, so let's do this manually */
				SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
				SimpleDateFormat formatter2 = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
				
				url = new URL(dataSource.getFeedUrl());
				Feed feed = null;
				try {
					feed = FeedParser.parse(url);
					
					DataTransferObject text;
					FeedItem item;
					for (int i = 0; i < feed.getItemCount(); i++) {
						if (counter == 0) {
							break;
						}
						
						item = feed.getItem(i);
						
						/* there are problems with obtaining right publication date */
						Date published = item.getPubDate();
						if (published == null) {
							for (int j = 0; j < item.getNodeCount(); j++) {
								if ("published".equals(((RawElement) item.getNode(j)).getName())
									|| "pubDate".equals(((RawElement) item.getNode(j)).getName())) {
									String s = ((RawElement) item.getNode(j)).getValue();
									if (s != null) {
										try {
											published = formatter1.parse(s);
										} catch (ParseException pe1) {
											try {
												published = formatter2.parse(s);
											} catch (ParseException pe2) {
												logger.info("published date parse exception");
											}
										}
									}
									break;
								}
							}
						}
						if (published == null) {
							published = new Date();
						}
						
						/* accept only those feeds that after 1st May 2010 */
						if (published.after(startDate.getTime())) {
							text = new DataTransferObject();
							text.setDataSource(dataSource);
							text.setHeading(item.getTitle());							
							text.setText(item.getDescriptionAsText());							
							text.setSkipped(false);
							text.setTime(published);
							
							if (text.getHeading() != null
								&& text.getHeading().length() > 30) {
								text.setHeading(text.getHeading().substring(0, 30) + "...");
							}
							
							if (!dataService.exists(text)) {
								SOProcessingOrder order = new SOProcessingOrder();
								order.setHeading(text.getHeading());
								order.setTime(text.getTime());
								order.setText(text.getText());
								order.setDataSourceId(text.getDataSource().getId());
								orders.add(order);
								
								logger.info("feed added to order list");
								logger.info("feed published date = " + formatter1.format(text.getTime()));
								logger.info("feed url = " + item.getLink());
								logger.info("feed title = " + text.getHeading());
								
								counter--;
								total++;
							}																																
						}
					}
				} catch (Exception e) {
					errors += "<li>" + e.getMessage() + "</li>";		
					e.printStackTrace();				
				}						
			}
			
			
			if (orders != null
				&& orders.size() > 0) {
				HttpURLConnection conn = null;
				
				boolean selfThrown = false;
				try {
					XStream xstream = new XStream();
					xstream.alias("processingOrder", SOProcessingOrder.class);
					String xml = xstream.toXML(orders.toArray(new SOProcessingOrder[0]));
					
					logger.info("xml = " + xml);
										
					url = new URL("http", SEMANTIC_ORIENTATION_WS_HOST, SEMANTIC_ORIENTATION_WS_PORT, "/getSemanticOrientation/order");
					conn = (HttpURLConnection) url.openConnection();
					/* to make sure that web-service reads text correctly */
					conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
					conn.setRequestMethod("POST");
					conn.setDoOutput(true);
					conn.connect();
										
					OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), Charset.forName("UTF-8"));
					wr.write(xml);
					wr.flush();
					
					if (conn.getResponseCode() == 503) {
						selfThrown = true;
						throw new Exception("Service is processing oredered data");
					} else if (conn.getResponseCode() != 200) {
						selfThrown = true;
						throw new Exception("HTTP status code is " + conn.getResponseCode());
					}
				} catch (Exception e) {
					if (conn != null) {
						conn.disconnect();
					}
					if (e.getMessage() != null
						&& e.getMessage().indexOf("Timeout") != -1) {
					} else {		
						errors += "<li>" + e.getMessage() + "</li>";
						if (!selfThrown) {
							e.printStackTrace();
						}
					}
				}
			}
					
			if ("".equals(errors)) {
				logger.info("data successfully ordered");
			} else {
				logger.info("some errors occured during data ordering");
			}
			resp.getWriter().println(
				"<html>" +
					"<title>inmyhumbleopinion.lv | Data ordering</title>" +
					"<body>" +
						(!"".equals(errors) ? "<h2>Some errors occured during the data population (refer to logs)</h2><ol>" + errors + "</ol>" : 
							"<h2>Feeds were successfully sent for processing!</h2><p>" + total + " new feeds</p>") +
					"</body>" +
				"</html>"				
			);
		} else {
			logger.info("there are no data sources");
			resp.getWriter().println(
				"<html>" +
					"<title>inmyhumbleopinion.lv | Data ordering</title>" +
					"<body>" +
						"<h2>There are no data sources</h2>" +
					"</body>" +
				"</html>"				
			);
		}		
	}

}
